
Portifólio Thiago Gonçalves Custódio
Através de modelagem preditiva financeira prever comportamento para os próximos dias do Ativo Financeiro Vale3.
Cotações reais do ativo Vale3 no intervalo diário:
https://www.b3.com.br/pt_br/market-data-e-indices/servicos-de-dados/market-data/cotacoes/
!pip install -q -U watermark
# Imports para manipulação de dados
import numpy as np
import pandas as pd
# Imports para visualização
import matplotlib.pyplot as plt
import matplotlib as m
import seaborn as sns
import plotly.graph_objects as go
# Imports para cálculos estatísticos
import scipy
from scipy.stats import kurtosis, skew, shapiro
import warnings
warnings.filterwarnings("ignore")
# Imports para formatação dos gráficos
plt.style.use('fivethirtyeight')
m.rcParams['axes.labelsize'] = 14
m.rcParams['xtick.labelsize'] = 12
m.rcParams['ytick.labelsize'] = 12
m.rcParams['text.color'] = 'k'
from matplotlib.pylab import rcParams
rcParams['figure.figsize'] = 20,10
# Versões dos pacotes usados neste jupyter notebook
%reload_ext watermark
%watermark -a "Thiago Gonçalves Custódio" --iversions
# Carrega o dataset
dados = pd.read_excel("dados/Vale3.xlsx", parse_dates = True, index_col = "Date")
# Visualizando registros
# Cada coluna representa o valor da ação em cada dia da série.
# valor de abertura, fechamento, máximo, mínimo e volume.
# A coluna Mudanca(%) representa a variação diária.
dados.head()
# Tipos de Dados
dados.dtypes
# Shape
dados.shape
# Sumário estatístico
dados.describe()
# Figura
fig = go.Figure(data = [go.Candlestick(x = dados.index,
open = dados['Abertura'],
high = dados['Máxima'],
low = dados['Mínima'],
close = dados['Fechamento'])])
# Layout
fig.update_layout(title = "Candlestick Series do Preço de Ações da Empresa Vale3 ")
# Gráfico
fig.show()
# Plot
plt.plot(dados["Fechamento"])
plt.title("Preço Diário de Fechamento das Ações", size = 14)
plt.show()
Calculo do retorno diário da série.
# Calculando o percentual de mudança na cotação de fechamento diário das ações
# Ou seja, quanto o valor de fechamento varia de um dia para outro, o retorno diário da ação
retorno_diario = dados["Fechamento"].pct_change().dropna()
retorno_diario.head()
Calculo do retorno acumulado da série.
# Retorno acumulado
retorno_diario_acumulado = (1 + retorno_diario).cumprod() - 1
retorno_diario_acumulado.max()
Vamos usar a estatística para calcular o retorno médio e a variação (desvio padrão).
# Média do fechamento diário da cotação das ações
media_retorno_diario = np.mean(retorno_diario)
# Desvio padrão do fechamento diário da cotação das ações
desvio_retorno_diario = np.std(retorno_diario)
# Média e desvio padrão
print("Média do Retorno de Fechamento:", media_retorno_diario)
print("Desvio Padrão do Retorno de Fechamento:", desvio_retorno_diario)
Vamos considerar o ano com 252 dias de funcionamento da bolsa brasileira
# Média e desvio padrão no ano (considerando 252 dias úteis de operações na bolsa americana)
print("Retorno Médio Anualizado de Fechamento:", (1 + media_retorno_diario) ** 252 - 1)
print("Desvio Padrão Anualizado de Fechamento:", desvio_retorno_diario*np.sqrt(252))
Retorno médio anualizado mostrou-se negativo.
# Plot
plt.plot(retorno_diario)
plt.title("Retorno Diário", size = 14)
plt.show()
# Plot
plt.hist(retorno_diario, bins = 75)
plt.title("Histograma do Retorno Diário", size = 14)
plt.show()
Os valores estão bem próximos mesmo da média. Mas vamos confirmar isso calculando curtose e assimetria.
# Calculando curtose e assimetria
print("Curtose:", kurtosis(retorno_diario))
print("Assimetria:", skew(retorno_diario))
A curtose indica que os registros estão bem próximos da média. Mas a assimetria indica que os dados podem estar distorcidos e distantes de uma distribuição normal. Vamos aplicar o teste de normalidade na série.
# Executa o teste de normalidade para a série
teste_normalidade = shapiro(retorno_diario)[1]
# Verifica o retorno com base no valor-p de 0.05
if teste_normalidade <= 0.05:
print("Rejeitamos a Hipótese Nula de Normalidade dos Dados.")
else:
print("Falhamos em Rejeitar a Hipótese Nula de Normalidade dos Dados.")
A distribuição não é normal. Vamos aplicar uma transformação de log à série e então aplicar a técnica de diferenciação para retirar da série os padrões de tendência e deixarmos apenas os dados reais, que nos interessam. Com isso calculamos o retorno diário.
# Transformação de log e diferenciação para cálculo do retorno diário
log_retorno_diario = (np.log(dados["Fechamento"]) - np.log(dados["Fechamento"]).shift(-1)).dropna()
# Calculamos média e desvio padrão após a transformação
log_media_retorno_diario = np.mean(log_retorno_diario)
log_desvio_retorno_diario = np.std(log_retorno_diario)
Vamos criar um plot com o retorno diário da série transformada.
# Plot
plt.plot(log_retorno_diario)
plt.title("Retorno Diário (Log Transformation)", size = 14)
plt.show()
# Plot
plt.hist(log_retorno_diario, bins = 75)
plt.title("Histograma do Retorno Diário (Log Transformation)", size = 14)
plt.show()
# Calculando curtose e assimetria
print("Curtose:", kurtosis(log_retorno_diario))
print("Assimetria:", skew(log_retorno_diario))
# Executa o teste de normalidade para a série
teste_normalidade = shapiro(log_retorno_diario)[1]
# Verifica o retorno com base no valor-p de 0.05
if teste_normalidade <= 0.05:
print("Rejeitamos a Hipótese Nula de Normalidade dos Dados.")
else:
print("Falhamos em Rejeitar a Hipótese Nula de Normalidade dos Dados.")
Os dados ainda não estão normais, porém reduzimos significativamente a distorção dos dados. Poderíamos aplicar outras transformações, mas para o objetivo este estudo isso é suficiente. Seguimos com a série transformada.
Vamos calcular o valor histórico do preço da ação.
# Nível de variância
var_level = 95
var = np.percentile(log_retorno_diario, 100 - var_level)
print("Certeza de que as perdas diárias não excederão o VaR% em um determinado dia com base em valores históricos.")
print("VaR 95%:", var)
# Var para os próximos 5 dias
var * np.sqrt(5)
# Nível de variância
var_level = 95
var = np.percentile(log_retorno_diario, 100 - var_level)
cvar = log_retorno_diario[log_retorno_diario < var].mean()
print("Nos piores 5% dos casos, as perdas foram, em média, superiores ao percentual histórico.")
print("CVaR 95%:", cvar)
# Simulação de Monte Carlo
# Número de dias a frente
dias_posteriores = 252
# Número de simulações
simulacoes = 2500
# Último valor da ação
ultimo_preco = 69.43
# Cria um array vazio com as dimensões
results = np.empty((simulacoes, dias_posteriores))
# Loop por cada simulação
for s in range(simulacoes):
# Calcula o retorno com dados randômicos seguindo uma distribuição normal
random_returns = 1 + np.random.normal(loc = log_media_retorno_diario,
scale = log_desvio_retorno_diario,
size = dias_posteriores)
result = ultimo_preco * (random_returns.cumprod())
results[s, :] = result
# Definindo o índice da série simulada
index = pd.date_range("2021-12-01", periods = dias_posteriores, freq = "D")
resultados = pd.DataFrame(results.T, index = index)
media_resultados = resultados.apply("mean", axis = 1)
# Dividindo a área de plotagem em 2 subplots
fig, ax = plt.subplots(nrows = 2, ncols = 1)
# Plot
ax[0].plot(dados["Fechamento"][:"2018-12-31"])
ax[0].plot(resultados)
ax[0].axhline(69.43, c = "orange")
ax[0].set_title(f"Monte Carlo {simulacoes} Simulações", size = 14)
ax[0].legend(["Preço Histórico", "Último Preço = 69.43"])
ax[1].plot(dados["Fechamento"][:"2018-12-31"])
ax[1].plot(resultados.apply("mean", axis = 1), lw = 2)
ax[1].plot(media_resultados.apply((lambda x: x * (1+1.96 * log_desvio_retorno_diario))),
lw = 2, linestyle = "dotted", c = "gray")
ax[1].plot(media_resultados, lw = 2, c = "orange")
ax[1].plot(media_resultados.apply((lambda x: x * (1-1.96 * log_desvio_retorno_diario))),
lw = 2, linestyle = "dotted", c = "gray")
ax[1].set_title(f"Resultado Médio Monte Carlo {simulacoes} Simulações", size = 14)
ax[1].legend(["Preço", "Previsão Média", "2x Desvio Padrao"])
plt.show()
A previsão é positiva com os dados simulados e no longo prazo as ações da VALE3 tendem a valorizar.
Observação importante: Não sou analista de valores imobiliarios, a analise acima reflete apenas a minha opnião pessoal baseada nos estudos neste projeto realizados.
Caso tenha alguma dúvida ou sugestão, entre em contato.